home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Internet Info 1994 March
/
Internet Info CD-ROM (Walnut Creek) (March 1994).iso
/
networking
/
ip
/
ka9q
/
src.arc
/
IPROUTE.C
< prev
next >
Wrap
C/C++ Source or Header
|
1989-08-19
|
15KB
|
588 lines
/* Lower half of IP, consisting of gateway routines
* Includes routing and options processing code
*/
#include "global.h"
#include "mbuf.h"
#include "internet.h"
#include "timer.h"
#include "netuser.h"
#include "ip.h"
#include "icmp.h"
#include "iface.h"
#include "trace.h"
static int16 hash_ip __ARGS((int32 addr));
static struct route *rt_lookup __ARGS((int32 target));
struct route *Routes[32][NROUTE]; /* Routing table */
struct route R_default; /* Default route entry */
int32 Ip_addr;
struct ip_stats Ip_stats;
/* Route an IP datagram. This is the "hopper" through which all IP datagrams,
* coming or going, must pass.
*
* "rxbroadcast" is set to indicate that the packet came in on a subnet
* broadcast. The router will kick the packet upstairs regardless of the
* IP destination address.
*/
int
ip_route(bp,rxbroadcast)
struct mbuf *bp;
int rxbroadcast; /* True if packet had link broadcast address */
{
struct ip ip; /* IP header being processed */
int16 ip_len; /* IP header length */
int16 length; /* Length of data portion */
int32 gateway; /* Gateway IP address */
register struct route *rp; /* Route table entry */
struct iface *iface; /* Output interface, possibly forwarded */
int16 offset; /* Offset into current fragment */
int16 mf_flag; /* Original datagram MF flag */
int strict = 0; /* Strict source routing flag */
char prec; /* Extracted from tos field */
char del;
char tput;
char rel;
int16 opt_len; /* Length of current option */
char *opt; /* -> beginning of current option */
char *ptr; /* -> pointer field in source route fields */
struct mbuf *tbp;
Ip_stats.total++;
if(len_mbuf(bp) < IPLEN){
/* The packet is shorter than a legal IP header */
Ip_stats.runt++;
free_p(bp);
return -1;
}
/* Sneak a peek at the IP header's IHL field to find its length */
ip_len = (bp->data[0] & 0xf) << 2;
if(ip_len < IPLEN){
/* The IP header length field is too small */
Ip_stats.length++;
free_p(bp);
return -1;
}
if(cksum(NULLHEADER,bp,ip_len) != 0){
/* Bad IP header checksum; discard */
Ip_stats.checksum++;
free_p(bp);
return -1;
}
/* Extract IP header */
ntohip(&ip,&bp);
if(ip.version != IPVERSION){
/* We can't handle this version of IP */
Ip_stats.version++;
free_p(bp);
return -1;
}
/* Trim data segment if necessary. */
length = ip.length - ip_len; /* Length of data portion */
trim_mbuf(&bp,length);
/* Process options, if any. Also compute length of secondary IP
* header in case fragmentation is needed later
*/
strict = 0;
for(opt = ip.options; opt < &ip.options[ip.optlen];opt += opt_len){
/* Most options have a length field. If this is a EOL or NOOP,
* this (garbage) value won't be used
*/
opt_len = uchar(opt[1]);
switch(opt[0] & OPT_NUMBER){
case IP_EOL:
goto no_opt; /* End of options list, we're done */
case IP_NOOP:
opt_len = 1;
break; /* No operation, skip to next option */
case IP_SSROUTE: /* Strict source route & record route */
strict = 1; /* note fall-thru */
case IP_LSROUTE: /* Loose source route & record route */
/* Source routes are ignored unless we're in the
* destination field
*/
if(ip.dest != Ip_addr)
break; /* Skip to next option */
if(uchar(opt[2]) >= opt_len){
break; /* Route exhausted; it's for us */
}
/* Put address for next hop into destination field,
* put our address into the route field, and bump
* the pointer
*/
ptr = opt + uchar(opt[2]) - 1;
ip.dest = get32(ptr);
put32(ptr,Ip_addr);
opt[2] += 4;
break;
case IP_RROUTE: /* Record route */
if(uchar(opt[2]) >= opt_len){
/* Route area exhausted; kick back an error */
union icmp_args icmp_args;
icmp_args.pointer = IPLEN + opt - ip.options;
icmp_output(&ip,bp,PARAM_PROB,0,&icmp_args);
free_p(bp);
return -1;
}
/* Add our address to the route */
ptr = opt + uchar(opt[2]) - 1;
ptr = put32(ptr,Ip_addr);
opt[2] += 4;
break;
}
}
no_opt:
/* See if it's a broadcast or addressed to us, and kick it upstairs */
if(ip.dest == Ip_addr || rxbroadcast){
#ifdef GWONLY
/* We're only a gateway, we have no host level protocols */
if(!rxbroadcast)
icmp_output(&ip,bp,DEST_UNREACH,PROT_UNREACH,(union icmp_args *)NULL);
free_p(bp);
#else
ip_recv(&ip,bp,rxbroadcast);
#endif
return 0;
}
/* Decrement TTL and discard if zero */
if(--ip.ttl == 0){
/* Send ICMP "Time Exceeded" message */
icmp_output(&ip,bp,TIME_EXCEED,0,NULLICMP);
free_p(bp);
return -1;
}
/* Look up target address in routing table */
if((rp = rt_lookup(ip.dest)) == NULLROUTE){
/* No route exists, return unreachable message */
icmp_output(&ip,bp,DEST_UNREACH,HOST_UNREACH,NULLICMP);
free_p(bp);
return -1;
}
rp->uses++;
/* Check for output forwarding and divert if necessary */
iface = rp->iface;
if(iface->forw != NULLIF)
iface = iface->forw;
/* Find gateway; zero gateway in routing table means "send direct" */
if(rp->gateway == (int32)0)
gateway = ip.dest;
else
gateway = rp->gateway;
if(strict && gateway != ip.dest){
/* Strict source routing requires a direct entry */
icmp_output(&ip,bp,DEST_UNREACH,ROUTE_FAIL,NULLICMP);
free_p(bp);
return -1;
}
prec = PREC(ip.tos);
del = ip.tos & DELAY;
tput = ip.tos & THRUPUT;
rel = ip.tos & RELIABILITY;
if(ip.length <= iface->mtu){
/* Datagram smaller than interface MTU; put header
* back on and send normally
*/
if((tbp = htonip(&ip,bp)) == NULLBUF){
free_p(bp);
return -1;
}
return (*iface->send)(tbp,iface,gateway,prec,del,tput,rel);
}
/* Fragmentation needed */
if(ip.flags.df){
/* Don't Fragment set; return ICMP message and drop */
icmp_output(&ip,bp,DEST_UNREACH,FRAG_NEEDED,NULLICMP);
free_p(bp);
return -1;
}
/* Create fragments */
offset = ip.offset;
mf_flag = ip.flags.mf; /* Save original MF flag */
while(length != 0){ /* As long as there's data left */
int16 fragsize; /* Size of this fragment's data */
struct mbuf *f_data; /* Data portion of fragment */
/* After the first fragment, should remove those
* options that aren't supposed to be copied on fragmentation
*/
ip.offset = offset;
if(length + ip_len <= iface->mtu){
/* Last fragment; send all that remains */
fragsize = length;
ip.flags.mf = mf_flag; /* Pass original MF flag */
} else {
/* More to come, so send multiple of 8 bytes */
fragsize = (iface->mtu - ip_len) & 0xfff8;
ip.flags.mf = 1;
}
ip.length = fragsize + ip_len;
/* Duplicate the fragment */
dup_p(&f_data,bp,offset,fragsize);
if(f_data == NULLBUF){
free_p(bp);
return -1;
}
/* Put IP header back on */
if((tbp = htonip(&ip,f_data)) == NULLBUF){
free_p(f_data);
free_p(bp);
return -1;
}
/* and ship it out */
if((*iface->send)(tbp,iface,gateway,prec,del,tput,rel) == -1){
free_p(bp);
return -1;
}
offset += fragsize;
length -= fragsize;
}
free_p(bp);
return 0;
}
static struct rt_cache Rt_cache;
/* Add an entry to the IP routing table. Returns 0 on success, -1 on failure */
int
rt_add(target,bits,gateway,iface)
int32 target; /* Target IP address prefix */
unsigned int bits; /* Size of target address prefix in bits (0-32) */
int32 gateway;
struct iface *iface;
{
struct route *rp,**hp;
int16 i;
int32 mask;
if(iface == NULLIF)
return -1;
Rt_cache.target = 0; /* Flush cache */
/* Zero bits refers to the default route */
if(bits == 0){
rp = &R_default;
} else {
if(bits > 32)
bits = 32;
/* Mask off don't-care bits */
mask = 0xffffffff;
for(i=31;i >= bits;i--)
mask <<= 1;
target &= mask;
/* Search appropriate chain for existing entry */
for(rp = Routes[bits-1][hash_ip(target)];rp != NULLROUTE;rp = rp->next){
if(rp->target == target)
break;
}
}
if(rp == NULLROUTE){
/* The target is not already in the table, so create a new
* entry and put it in.
*/
if((rp = (struct route *)malloc(sizeof(struct route))) == NULLROUTE)
return -1; /* No space */
/* Insert at head of table */
rp->prev = NULLROUTE;
hp = &Routes[bits-1][hash_ip(target)];
rp->next = *hp;
if(rp->next != NULLROUTE)
rp->next->prev = rp;
*hp = rp;
}
rp->target = target;
rp->gateway = gateway;
rp->iface = iface;
rp->uses = 0;
return 0;
}
/* Remove an entry from the IP routing table. Returns 0 on success, -1
* if entry was not in table.
*/
int
rt_drop(target,bits)
int32 target;
unsigned int bits;
{
register struct route *rp;
unsigned int i;
int32 mask;
Rt_cache.target = 0; /* Flush the cache */
if(bits == 0){
/* Nail the default entry */
R_default.iface = NULLIF;
return 0;
}
if(bits > 32)
bits = 32;
/* Mask off don't-care bits */
mask = 0xffffffff;
for(i=31;i >= bits;i--)
mask <<= 1;
target &= mask;
/* Search appropriate chain for existing entry */
for(rp = Routes[bits-1][hash_ip(target)];rp != NULLROUTE;rp = rp->next){
if(rp->target == target)
break;
}
if(rp == NULLROUTE)
return -1; /* Not in table */
if(rp->next != NULLROUTE)
rp->next->prev = rp->prev;
if(rp->prev != NULLROUTE)
rp->prev->next = rp->next;
else
Routes[bits-1][hash_ip(target)] = rp->next;
free((char *)rp);
return 0;
}
/* Compute hash function on IP address */
static int16
hash_ip(addr)
register int32 addr;
{
register unsigned int ret;
ret = hiword(addr);
ret ^= loword(addr);
return ret % NROUTE;
}
#ifndef GWONLY
/* Given an IP address, return the MTU of the local interface used to
* reach that destination. This is used by TCP to avoid local fragmentation
*/
int16
ip_mtu(addr)
int32 addr;
{
register struct route *rp;
struct iface *iface;
rp = rt_lookup(addr);
if(rp == NULLROUTE || rp->iface == NULLIF)
return 0;
iface = rp->iface;
if(iface->forw != NULLIF)
return iface->forw->mtu;
else
return iface->mtu;
}
#endif
/* Look up target in hash table, matching the entry having the largest number
* of leading bits in common. Return default route if not found;
* if default route not set, return NULLROUTE
*/
static struct route *
rt_lookup(target)
int32 target;
{
register struct route *rp;
int bits;
int32 tsave;
int32 mask;
if(target == Rt_cache.target)
return Rt_cache.route;
tsave = target;
mask = ~0; /* All ones */
for(bits = 31;bits >= 0; bits--){
target &= mask;
for(rp = Routes[bits][hash_ip(target)];rp != NULLROUTE;rp = rp->next){
if(rp->target == target){
/* Stash in cache and return */
Rt_cache.target = tsave;
Rt_cache.route = rp;
return rp;
}
}
mask <<= 1;
}
if(R_default.iface != NULLIF){
Rt_cache.target = tsave;
Rt_cache.route = &R_default;
return &R_default;
} else
return NULLROUTE;
}
/* Convert IP header in host format to network mbuf */
struct mbuf *
htonip(ip,data)
struct ip *ip;
struct mbuf *data;
{
int16 hdr_len;
struct mbuf *bp;
register char *cp;
int16 checksum;
int16 fl_offs;
hdr_len = IPLEN + ip->optlen;
if((bp = pushdown(data,hdr_len)) == NULLBUF){
free_p(data);
return NULLBUF;
}
cp = bp->data;
*cp++ = (IPVERSION << 4) | (hdr_len >> 2);
*cp++ = ip->tos;
cp = put16(cp,ip->length);
cp = put16(cp,ip->id);
fl_offs = ip->offset >> 3;
if(ip->flags.df)
fl_offs |= 0x4000;
if(ip->flags.mf)
fl_offs |= 0x2000;
cp = put16(cp,fl_offs);
*cp++ = ip->ttl;
*cp++ = ip->protocol;
cp = put16(cp,0); /* Clear checksum */
cp = put32(cp,ip->source);
cp = put32(cp,ip->dest);
if(ip->optlen != 0)
memcpy(cp,ip->options,ip->optlen);
/* Compute checksum and insert into header */
checksum = cksum(NULLHEADER,bp,hdr_len);
put16(&bp->data[10],checksum);
return bp;
}
/* Extract an IP header from mbuf */
int
ntohip(ip,bpp)
struct ip *ip;
struct mbuf **bpp;
{
int16 ihl;
int16 fl_offs;
char ipbuf[IPLEN];
if(pullup(bpp,ipbuf,IPLEN) != IPLEN)
return -1;
ip->version = (ipbuf[0] >> 4) & 0xf;
ip->tos = ipbuf[1];
ip->length = get16(&ipbuf[2]);
ip->id = get16(&ipbuf[4]);
fl_offs = get16(&ipbuf[6]);
ip->offset = (fl_offs & 0x1fff) << 3;
ip->flags.mf = (fl_offs & 0x2000) ? 1 : 0;
ip->flags.df = (fl_offs & 0x4000) ? 1 : 0;
ip->ttl = ipbuf[8];
ip->protocol = ipbuf[9];
ip->source = get32(&ipbuf[12]);
ip->dest = get32(&ipbuf[16]);
ihl = (ipbuf[0] & 0xf) << 2;
if(ihl < IPLEN){
/* Bogus packet; header is too short */
return -1;
}
ip->optlen = ihl - IPLEN;
if(ip->optlen != 0)
pullup(bpp,ip->options,ip->optlen);
return ip->optlen + IPLEN;
}
/* Perform end-around-carry adjustment */
int16
eac(sum)
register int32 sum; /* Carries in high order 16 bits */
{
register int16 csum;
while((csum = sum >> 16) != 0)
sum = csum + (sum & 0xffffL);
return (int16) (sum & 0xffffl); /* Chops to 16 bits */
}
/* Checksum a mbuf chain, with optional pseudo-header */
int16
cksum(ph,m,len)
struct pseudo_header *ph;
register struct mbuf *m;
int16 len;
{
register unsigned int cnt, total;
register int32 sum, csum;
register char *up;
int16 csum1;
int swap = 0;
sum = 0l;
/* Sum pseudo-header, if present */
if(ph != NULLHEADER){
sum = hiword(ph->source);
sum += loword(ph->source);
sum += hiword(ph->dest);
sum += loword(ph->dest);
sum += uchar(ph->protocol);
sum += ph->length;
}
/* Now do each mbuf on the chain */
for(total = 0; m != NULLBUF && total < len; m = m->next) {
cnt = min(m->cnt, len - total);
up = (char *)m->data;
csum = 0;
if(((long)up) & 1){
/* Handle odd leading byte */
if(swap)
csum = uchar(*up++);
else
csum = (int16)(uchar(*up++) << 8);
cnt--;
swap = !swap;
}
if(cnt > 1){
/* Have the primitive checksumming routine do most of
* the work. At this point, up is guaranteed to be on
* a short boundary
*/
csum1 = lcsum((unsigned short *)up, cnt >> 1);
if(swap)
csum1 = (csum1 << 8) | (csum1 >> 8);
csum += csum1;
}
/* Handle odd trailing byte */
if(cnt & 1){
if(swap)
csum += uchar(up[--cnt]);
else
csum += (int16)(uchar(up[--cnt]) << 8);
swap = !swap;
}
sum += csum;
total += m->cnt;
}
/* Do final end-around carry, complement and return */
return ~eac(sum) & 0xffff;
}